package com.easygem.system.shiro;

import com.easygem.common.error.SystemCodeEnum;
import com.easygem.common.error.SystemException;
import com.easygem.common.model.system.Employee;
import com.easygem.common.model.system.Menu;
import com.easygem.common.model.system.Role;
import com.easygem.common.model.system.User;
import com.easygem.common.response.ActiveUser;
import com.easygem.system.jwt.JWTToken;
import com.easygem.system.jwt.JWTUtils;
import com.easygem.system.service.EmployeeService;
import com.easygem.system.service.UserService;
import com.easygem.system.util.MD5Utils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
@Slf4j
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private EmployeeService employeeService;

    /**
     * 大坑！，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        //为了让realm支持jwt的凭证校验
        return token instanceof JWTToken;
    }

    /**
     * 只有当需要检测用户权限的时候才会调用此方法，例如checkRole,checkPermission之类的
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //创建授权信息
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //获取用户名
        ActiveUser activeUser = (ActiveUser) SecurityUtils.getSubject().getPrincipal();

        if(activeUser.getEmployee().getType()==0){
            authorizationInfo.addStringPermission("*:*");
        }else {
            List<String> permissions = new ArrayList<>(activeUser.getPermissions());
            List<Role> roleList = activeUser.getRoles();
            //授权角色
            if (!CollectionUtils.isEmpty(roleList)) {
                for (Role role : roleList) {
                    authorizationInfo.addRole(role.getRoleName());
                }
            }
            //授权权限
            if (!CollectionUtils.isEmpty(permissions)) {
                for (String  permission : permissions) {
                    if (permission != null && !"".equals(permission)) {
                        authorizationInfo.addStringPermission(permission);
                    }
                }
            }
        }
        return authorizationInfo;
    }

    /**
     * 默认使用此方法进行用户名正确与否验证，错误抛出异常即可。
     * 认证
     */
    @SneakyThrows
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getCredentials();
        // 解密获得username，用于和数据库进行对比
        String username = JWTUtils.getUsername(token);
        String accountName = JWTUtils.getAccountName(token);

        if (username == null) {
            throw new AuthenticationException(" token错误，请重新登入！");
        }

        Employee employeeBean = employeeService.findEmployeeByName(username);

        if (employeeBean == null) {
            throw new AccountException("账号不存在!");
            //redis中在设置一次
            //redisUtil.setValue(Constant.PREFIX_SHIRO_USER + user.getId(), user, refreshTokenExpireTime);

        }
        if(JWTUtils.isExpire(token)){
            throw new AuthenticationException(" token过期，请重新登入！");
        }
        // 校验token是否超时失效 & 或者账号密码是否错误
        /*if (!jwtTokenRefresh(token, username, "123")) {
            throw new AuthenticationException("Token失效，请重新登录!");
        }*/

        if (!JWTUtils.verify(token, username, employeeBean.getPassword())) {
            throw new CredentialsException("密码错误!");
        }

        if(employeeBean.getStatus()==-1){
            throw new LockedAccountException("账号已被锁定!");
        }

        //如果验证通过，获取用户的角色
       // List<Role> roles= employeeService.findRolesById(employeeBean.getEmpId());
        //查询用户的所有菜单(包括了菜单和按钮)
        //List<Menu> menus=employeeService.findMenuByRoles(roles);


        //如果验证通过，获取用户的角色
        //List<Role> roles= employeeService.findRolesById(userBean.getUserId());
        Role role = employeeService.findRoleById(employeeBean.getEmpId());
        //查询用户的所有菜单(包括了菜单和按钮)
        //List<Menu> menus=employeeService.findMenuByRoles(roles);
        List<Menu> menus=employeeService.findMenuByRole(role);

        Set<String> urls=new HashSet<>();
        Set<String> perms=new HashSet<>();
        if(!CollectionUtils.isEmpty(menus)){
            for (Menu menu : menus) {
                String url = menu.getUrl();
                String per = menu.getPerms();
                if(menu.getMenuType()==0&& !StringUtils.isEmpty(url)){
                    urls.add(menu.getUrl());
                }
                if(menu.getMenuType()==1&&!StringUtils.isEmpty(per)){
                    perms.add(menu.getPerms());
                }
            }
        }
        //过滤出url,和用户的权限
        ActiveUser activeUser = new ActiveUser();
        activeUser.setRole(role);
        activeUser.setEmployee(employeeBean);
        activeUser.setMenus(menus);
        activeUser.setUrls(urls);
        activeUser.setPermissions(perms);
        log.info("查询出的角色为：{}",perms);
        return new SimpleAuthenticationInfo(activeUser, token, getName());
    }
    /**
     * jwt刷新令牌
     *
     * @param token    令牌
     * @param userName 用户名
     * @param passWord 通过单词
     * @return boolean
     */
    public boolean jwtTokenRefresh(String token, String userName,String accountName, String passWord) throws SystemException {
        //String redisToken = (String) redisTemplate.opsForValue().get(token);
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(token)) {
            //校验token是否正确
            if (!JWTUtils.verify(token, userName, passWord)) {
                Employee employee = employeeService.findEmployeeByName(userName);
                if (employee != null) {
                    String salt = employee.getSalt();
                    //秘钥为盐
                    String target = MD5Utils.md5Encryption(passWord, salt);
                    //生成Token
                    token = JWTUtils.sign(userName, target);
                    JWTToken jwtToken = new JWTToken(token);
                    try {
                        SecurityUtils.getSubject().login(jwtToken);
                    } catch (AuthenticationException e) {
                        throw new SystemException(SystemCodeEnum.PARAMETER_ERROR, e.getMessage());
                    }
                }
                //设置redis缓存
                //redisTemplate.opsForValue().set(token, newToken, expireTime * 2 / 1000, TimeUnit.SECONDS);
            }
            return true;
        }
        return false;
    }
}
